package main

import (
	"fmt"
	client "github.com/influxdata/influxdb1-client/v2"
	"github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/disk"
	"github.com/shirou/gopsutil/mem"
	"github.com/shirou/gopsutil/net"
	"log"
	"time"
)

var (
	cli                    client.Client
	lastNetIOStatTimeStamp int64    //上一次获取网络IO数据的时间点
	lastNetInfo            *NetInfo //上一次的网络IO数据
)

type CpuInfo struct {
	CpuPercent float64 `json:"cpu_percent"`
}

type MemInfo struct {
	Total       uint64  `json:"total"`
	Available   uint64  `json:"available"`
	Used        uint64  `json:"used"`
	UsedPercent float64 `json:"used_percent"`
	Buffers     uint64  `json:"buffers"`
	Cached      uint64  `json:"cached"`
}

type DiskInfo struct {
	PartitionUsageStat map[string]*disk.UsageStat
}

type IOStat struct {
	BytesSent       uint64
	BytesRecv       uint64
	PacketsSent     uint64
	PacketsRecv     uint64
	BytesSendRate   float64 `json:"bytes_sent_rate"`   // number of bytes sent
	BytesRecvRate   float64 `json:"bytes_recv_rate"`   // number of bytes received
	PacketsSentRate float64 `json:"packets_sent_rate"` // number of packets sent
	PacketsRecvRate float64 `json:"packets_recv_rate"` // number of packets received
}

type NetInfo struct {
	NetIOCountersStat map[string]*IOStat
}

func initConnInflux() (err error) {
	cli, err = client.NewHTTPClient(client.HTTPConfig{
		Addr:     "http://127.0.0.1:8086",
		Username: "admin",
		Password: "",
	})
	return
}

// insert
func writesCpuPoints(data *CpuInfo) {
	bp, err := client.NewBatchPoints(client.BatchPointsConfig{
		Database:  "monitor",
		Precision: "s", //精度，默认ns
	})
	if err != nil {
		log.Fatal(err)
	}
	//根据传入数据的类型插入数据

	tags := map[string]string{"cpu": "cpu0"}
	fields := map[string]interface{}{
		"cpu_percent": data.CpuPercent,
	}
	pt, err := client.NewPoint("cpu_percent", tags, fields, time.Now())
	if err != nil {
		log.Fatal(err)
	}
	bp.AddPoint(pt)
	err = cli.Write(bp)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("insert cpu success")
}

// cpu info
func getCpuInfo() {
	var cpuInfo = new(CpuInfo)
	// CPU使用率
	percent, _ := cpu.Percent(time.Second, false)
	fmt.Printf("cpu percent:%v\n", percent[0])
	cpuInfo.CpuPercent = percent[0]
	// insert
	writesCpuPoints(cpuInfo)
}

// mem info
func writesMemPoints(data *MemInfo) {
	bp, err := client.NewBatchPoints(client.BatchPointsConfig{
		Database:  "monitor",
		Precision: "s", //精度，默认ns
	})
	if err != nil {
		log.Fatal(err)
	}
	//根据传入数据的类型插入数据

	tags := map[string]string{"mem": "mem"}
	fields := map[string]interface{}{
		"total":        int64(data.Total),
		"available":    int64(data.Available),
		"used":         int64(data.Used),
		"used_percent": int64(data.UsedPercent),
	}
	pt, err := client.NewPoint("memory", tags, fields, time.Now())
	if err != nil {
		log.Fatal(err)
	}
	bp.AddPoint(pt)
	err = cli.Write(bp)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("insert memory success")
}

func getMemInfo() {
	//var memInfo = new(MemInfo)
	info, _ := mem.VirtualMemory()
	//fmt.Printf("mem info:%v\n", info)
	memInfo := &MemInfo{
		Total:       info.Total,
		Available:   info.Available,
		Used:        info.Used,
		UsedPercent: info.UsedPercent,
		Buffers:     info.Buffers,
		Cached:      info.Cached,
	}
	fmt.Printf("mem info:%v\n", memInfo)
	writesMemPoints(memInfo)
}

// disk info
func writesDiskPoints(data *DiskInfo) {
	bp, err := client.NewBatchPoints(client.BatchPointsConfig{
		Database:  "monitor",
		Precision: "s", //精度，默认ns
	})
	if err != nil {
		log.Fatal(err)
	}
	//根据传入数据的类型插入数据
	for k, v := range data.PartitionUsageStat {
		tags := map[string]string{"path": k}
		fields := map[string]interface{}{
			"total":               int64(v.Total),
			"free":                int64(v.Free),
			"used":                int64(v.Used),
			"used_percent":        int64(v.UsedPercent),
			"inodes_total":        int64(v.InodesTotal),
			"inodes_used":         int64(v.InodesUsed),
			"inodes_free":         int64(v.InodesFree),
			"inodes_used_percent": v.InodesUsedPercent,
		}
		pt, err := client.NewPoint("disk", tags, fields, time.Now())
		if err != nil {
			log.Fatal(err)
		}
		bp.AddPoint(pt)
	}

	err = cli.Write(bp)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("insert disk success")
}

func getDiskInfo() {
	var diskInfo = &DiskInfo{
		PartitionUsageStat: make(map[string]*disk.UsageStat, 16),
	}
	parts, _ := disk.Partitions(true)
	for _, part := range parts {
		//拿到每一个磁盘分区
		usageStat, err := disk.Usage(part.Mountpoint) //传挂载点
		if err != nil {
			fmt.Printf("get %s usage stat failed,err:%v", err)
			continue
		}
		diskInfo.PartitionUsageStat[part.Mountpoint] = usageStat
	}
	writesDiskPoints(diskInfo)
}

// net info
func writesNetPoints(data *NetInfo) {
	bp, err := client.NewBatchPoints(client.BatchPointsConfig{
		Database:  "monitor",
		Precision: "s", //精度，默认ns
	})
	if err != nil {
		log.Fatal(err)
	}
	//根据传入数据的类型插入数据
	for k, v := range data.NetIOCountersStat {
		tags := map[string]string{"name": k} //把每个网卡存为tag
		fields := map[string]interface{}{
			"bytes_sent_rate":   v.BytesSendRate,
			"bytes_recv_rate":   v.BytesRecvRate,
			"packets_sent_rate": v.PacketsSentRate,
			"packets_recv_rate": v.PacketsRecvRate,
		}
		pt, err := client.NewPoint("net", tags, fields, time.Now())
		if err != nil {
			log.Fatal(err)
		}
		bp.AddPoint(pt)
	}

	err = cli.Write(bp)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("insert net success")
}

func getNetInfo() {
	var netInfo = &NetInfo{
		NetIOCountersStat: make(map[string]*IOStat, 8),
	}
	currentTimeStamp := time.Now().Unix()
	netIOs, _ := net.IOCounters(true)
	for _, netIO := range netIOs {
		var ioStat = new(IOStat)
		ioStat.BytesSent = netIO.BytesSent
		ioStat.BytesRecv = netIO.BytesRecv
		ioStat.PacketsSent = netIO.PacketsSent
		ioStat.PacketsRecv = netIO.PacketsRecv
		//将具体网卡数据的ioStat变量添加到map中 不要放到continue下面
		netInfo.NetIOCountersStat[netIO.Name] = ioStat
		//开始计算网卡相关速率
		if lastNetIOStatTimeStamp == 0 || lastNetInfo == nil {
			continue
		}
		//计算时间间隔
		interval := currentTimeStamp - lastNetIOStatTimeStamp
		//计算速率
		ioStat.BytesSendRate = (float64(ioStat.BytesSent) - float64(lastNetInfo.NetIOCountersStat[netIO.Name].BytesSent)) / float64(interval)
		ioStat.BytesRecvRate = (float64(ioStat.BytesRecvRate) - float64(lastNetInfo.NetIOCountersStat[netIO.Name].BytesRecvRate)) / float64(interval)
		ioStat.PacketsSentRate = (float64(ioStat.PacketsSentRate) - float64(lastNetInfo.NetIOCountersStat[netIO.Name].PacketsSentRate)) / float64(interval)
		ioStat.PacketsRecvRate = (float64(ioStat.PacketsRecvRate) - float64(lastNetInfo.NetIOCountersStat[netIO.Name].PacketsRecvRate)) / float64(interval)

	}
	//更新全局记录的上一次采集时间点和网卡数据
	lastNetIOStatTimeStamp = currentTimeStamp //更新时间
	lastNetInfo = netInfo
	writesNetPoints(netInfo)
}

func run(interval time.Duration) {
	ticker := time.Tick(interval)
	for _ = range ticker {
		getCpuInfo()
		getMemInfo()
		getDiskInfo()
		getNetInfo()
	}
}

func main() {

	err := initConnInflux()
	if err != nil {
		fmt.Printf("connect to influxDB failed,err:%v", err)
	}
	run(time.Second)

}
